package com.eece411.project.p2;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;

public class ThreadSckServer implements Runnable {
	// Base protocol info
	private static final int KEY_LENGHT=32;
	private static final int DATA_LENGHT=1024;
	// Network obj
	private Socket csocket;
	// Time of creation of the thread
	private byte command;
	private long startTime;
	// Verbose var
	private String str="";
	
	// Constructor
	ThreadSckServer(byte command, Socket socket, long time) {
		this.csocket = socket;
		this.command = command;
		this.startTime = time;
	}
	public void run() {
	      try {
	    	  if (Routing.getInstance()!=null){
	    		  byte[] key = new byte[KEY_LENGHT];
	    		  int totalBytesRcvd=0;
	    		  // Read until the expected length of the header or timeout
	    		  while (totalBytesRcvd < key.length && ((System.currentTimeMillis()-startTime)<1000)) {
	    			  int bytesRcvd;
	    			  if ((bytesRcvd = this.csocket.getInputStream().read(key, totalBytesRcvd,  
	    					  key.length - totalBytesRcvd)) == -1)
	    				  throw new SocketException("Connection close prematurely");
	    			  totalBytesRcvd += bytesRcvd;
	    		  }
	    		  if (((System.currentTimeMillis()-startTime)<1000)){
	    			  if(((command & 0xFF) == 0x21)){
	    				  this.put(key);
			    	  } else if(((command & 0xFF) == 0x23)){
			    		  this.remove(key);
			    	  } else {
							// Command not found -> error 5
							csocket.getOutputStream().write((byte) 5);
							str+="--- Command not found || ";
							this.csocket.close();
			    	  }
	    		  } else {
		    		  // Timeout for client ->  Error 21
		    		  csocket.getOutputStream().write((byte) 21);
	                  str+="--- Kick client ||";
	                  this.csocket.close();
	    		  }

	    	  } else {
	    		  // DataStore Internal Error ->  Error 4
	    		  csocket.getOutputStream().write((byte) 4);
                  str+="--- KVStore Error || ";
                  this.csocket.close();
	    	  }

	      	} catch (IOException e) {
	      		str+="Error in the communication || ";
	      	} catch (OutOfMemoryError E) {
	      		System.err.println("Out of memory");
	      		try {
	      			csocket.getOutputStream().write((byte) 2);
	    		  	csocket.close();
	      		} catch (IOException e) {
	      			str+="Error in the communication || ";
	      		}
	     	}
	      System.out.println(str+="Req served in : "+(System.currentTimeMillis()-startTime)+" || Client connected : "+Routing.getInstance().getclientCount());
	}
	
	private void put(byte[] key) throws SocketException, IOException{
			// Bytes Array of the DATA
			byte[] data = new byte[DATA_LENGHT];
			int totalBytesRcvd2 = 0;
			// Read until the expected length of data or time out
			while (totalBytesRcvd2 < data.length) {
				int bytesRcvd;
				if ((bytesRcvd = this.csocket.getInputStream().read(data, totalBytesRcvd2,  
						data.length - totalBytesRcvd2)) == -1)
					throw new SocketException("Connection close prematurely");
				totalBytesRcvd2 += bytesRcvd;
			}
			str+="--- PUT ---- Sync || ";
			Routing.getInstance().putSync(key,data);
			this.csocket.close();
	}
	private void remove(byte[] key) throws IOException{
			str+="--- Remove ---- Sync  || ";
			Routing.getInstance().removeSync(key);
			this.csocket.close();
	}
}
